<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      canvas {
        border: 3px solid #ccc;
      }
      .canvas-container{
        position: absolute;
        top:0;
        left:0;
      }
      video{
        width:100%;
        height:100%;
      }
    </style>
  </head>
  <body>
    <img id="image" src="./image.jpg" alt="" />
    <div style="width:500px;height:288px;position:relative;">
      <!-- <video controls style="width:100%;">
        <source src="http://vfx.mtime.cn/Video/2019/02/04/mp4/190204084208765161.mp4" ></source>
      </video> -->
      <canvas id="canvas" width="300" height="300"></canvas>
    </div>
    <button onclick="handleReset()">reset</button>
    <button onclick="handleCompelete()">compelete</button>
    <script src="./fabric.min.js"></script>
    <script>
      var convasDefaultWidth = 500;
      //
      var image = new fabric.Image(document.getElementById("image"));
      var imageWidth = image.get("width");
      var canvasWidth =
        imageWidth > convasDefaultWidth ? convasDefaultWidth : imageWidth;
      //
      var radio = imageWidth / canvasWidth;
      var canvas = new fabric.Canvas("canvas", {
        width: canvasWidth,
        height: image.get("height") / radio,
        // backgroundImage: image.scaleToWidth(canvasWidth),
        backgroundColor: 'transparent',
        selection:false
      });
      //

      var points = [];

      // 当前是否可以落点
      var canAddPoint = true;
      // 已存在线段
      var existLines = [];
      // 绘制是否完成
      var isCompelete = false;

      var lineOption = {
        strokeWidth: 2,
        stroke: "green",
        fill: "green",
        selectable: false,
        evented: false
      };
      var dashLineOption = Object.assign({},lineOption,{strokeDashArray: [10, 15]})
      // 连接线
      var combineLineWithFirst = new fabric.Line([0, 0, 0, 0], dashLineOption);
      var combineLineWithSecond = new fabric.Line([0, 0, 0, 0], dashLineOption);
      // 用于回显
      var polygon = new fabric.Polygon([], {
        strokeWidth: 2,
        stroke: "green",
        fill: "transparent",
        objectCaching: false,
        // transparentCorners: false,
        // cornerColor: "blue",
      });
      canvas.add(combineLineWithFirst);
      canvas.add(combineLineWithSecond);
      canvas.add(polygon);
      // TODO: 是否需要回显
      if(true){
        var pointsAlready = [
          { x: 330.119, y: 88.746 },
          { x: 621.818, y: 156.362 },
          { x: 510.83, y: 357.801 },
          { x: 278.893, y: 274.69 },
        ];
        drowPolygon(pointsAlready.map(function(per){
          return {
            x: per.x / radio,
            y: per.y / radio
          }
        }))
      }else{
        // canvas事件初始化
        initCanvasEvent();
      }

      // 获取已存在线段
      function getLinesArr() {
        var linesArr = []; // [[x1,]]
        for (var i = 0; i < points.length - 1; i++) {
          var line = [
            points[i].x,
            points[i].y,
            points[i + 1].x,
            points[i + 1].y,
          ];
          linesArr.push(line);
        }
        return linesArr;
      }
      // 设置连接线
      function setLineWithPoint(line, p1, p2) {
        if (!p1 || !p2) {
          line.set({ x1: 0, y1: 0, x2: 0, y2: 0 });
          return;
        }
        line.set({ x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y });
      }

      function canvasHandleMouseMove(options) {
        var pointsLen = points.length;
        if (pointsLen === 0) return;
        var firstPoint = points[0];
        var lastPoint = points[pointsLen - 1];
        // 此时不能共线
        // if(pointsLen === 2){
        //   checkCross(options.pointer,firstPoint,firstPoint,lastPoint)
        // }
        if (pointsLen > 2) {
          var exlines = getLinesArr();
          var hasCross = false;
          for (var i = 0; i < exlines.length; i++) {
            var perline = exlines[i];
            var point1 = { x: perline[0], y: perline[1] };
            var point2 = { x: perline[2], y: perline[3] };
            if (
              checkCross(options.pointer, firstPoint, point1, point2) &&
              checkCross(options.pointer, lastPoint, point1, point2)
            ) {
              hasCross = true;
              break;
            } else if (i > 0 && i < exlines.length - 1 && (checkCross(options.pointer, firstPoint, point1, point2) ||
            checkCross(options.pointer, lastPoint, point1, point2))){
              hasCross = true;
              break;
            }
          }
        }
        if (!hasCross) {
          canAddPoint = true;
          setLineWithPoint(combineLineWithFirst, options.pointer, firstPoint);
          setLineWithPoint(combineLineWithSecond, options.pointer, lastPoint);
        } else {
          canAddPoint = false;
          setLineWithPoint(combineLineWithFirst);
          setLineWithPoint(combineLineWithSecond);
        }

        canvas.renderAll();
      }

      function canvasHandleMouseDown(options) {
        if (!canAddPoint) return;
        //
        var pointsLen = points.length;
        var lastPoint = points[pointsLen - 1];
        if (
          lastPoint &&
          options.pointer.x === lastPoint.x &&
          options.pointer.y === lastPoint.y
        )
          return;
        // console.log(options, options.pointer.x, options.pointer.y);
        points.push({
          x: +options.pointer.x.toFixed(3),
          y: +options.pointer.y.toFixed(3),
        });
        if (lastPoint) {
          var line = new fabric.Line(
            [lastPoint.x, lastPoint.y, options.pointer.x, options.pointer.y],
            lineOption
          );
          canvas.add(line);
          existLines.push(line);
        }

        canvas.requestRenderAll();
      }
      //
      function initCanvasEvent() {
        canvas.off("mouse:move", canvasHandleMouseMove);
        canvas.off("mouse:down", canvasHandleMouseDown);
        canvas.on("mouse:move", canvasHandleMouseMove);
        canvas.on("mouse:down", canvasHandleMouseDown);
      }
      function removeExLines(){
        existLines.forEach(function (exline) {
          canvas.remove(exline);
        });
        existLines = [];
      }
      // 重置
      function handleReset() {
        points = [];
        canAddPoint = true;
        removeExLines();
        // existLines.forEach(function (exline) {
        //   canvas.remove(exline);
        // });
        // existLines = [];
        var originPoint = { x: 0, y: 0 };
        setLineWithPoint(combineLineWithFirst, originPoint, originPoint);
        setLineWithPoint(combineLineWithSecond, originPoint, originPoint);

        initCanvasEvent();
        canvas.requestRenderAll();

        polygon.set("points", []);
        isCompelete = false;
      }
      // 完成
      function handleCompelete() {
        var pointsLen = points.length;
        if (pointsLen < 3 || isCompelete) return;

        setLineWithPoint(combineLineWithFirst);
        setLineWithPoint(combineLineWithSecond);
        // 用多边形展示更一致些?
        // 闭合线
        // var line = new fabric.Line(
        //   [
        //     points[0].x,
        //     points[0].y,
        //     points[pointsLen - 1].x,
        //     points[pointsLen - 1].y,
        //   ],
        //   {
        //     strokeWidth: 2,
        //     stroke: "green",
        //     fill: "green",
        //     selectable: false,
        //     evented: false,
        //   }
        // );
        // canvas.add(line);
        // existLines.push(line);
        removeExLines();
        polygon.set("points", points);
        canvas.requestRenderAll();

        canvas.off("mouse:move", canvasHandleMouseMove);
        canvas.off("mouse:down", canvasHandleMouseDown);
        isCompelete = true;
        // 点坐标比例转换
        var convert = points.map(function (per) {
          return {
            x: +(per.x * radio).toFixed(3),
            y: +(per.y * radio).toFixed(3),
          };
        });
        console.log(convert, imageWidth);
      }

      // function getIsSamePoint(p1,p2){
      //   return (p1.x === p2.x && p1.y === p2.y)
      // }

      // 计算向量叉乘
      function crossMul(v1, v2) {
        return v1.x * v2.y - v1.y * v2.x;
      }
      // 判断两条线段是否相交line1(p1,p2) line2(p3,p4)
      function checkCross(p1, p2, p3, p4) {
        var v1 = { x: p1.x - p3.x, y: p1.y - p3.y },
          v2 = { x: p2.x - p3.x, y: p2.y - p3.y },
          v3 = { x: p4.x - p3.x, y: p4.y - p3.y },
          v = crossMul(v1, v3) * crossMul(v2, v3);
        v1 = { x: p3.x - p1.x, y: p3.y - p1.y };
        v2 = { x: p4.x - p1.x, y: p4.y - p1.y };
        v3 = { x: p2.x - p1.x, y: p2.y - p1.y };
        return v <= 0 && crossMul(v1, v3) * crossMul(v2, v3) <= 0
          ? true
          : false;
      }

      // 多边形回显
      function drowPolygon(points) {
        isCompelete = true;
        polygon.set("points", points);
      }

    </script>
  </body>
</html>
